/**
 * 格式化器标准化函数。
 * 该函数接受一个格式化器参数，并基于该参数的类型返回一个合适的格式化函数。
 * 如果参数是函数，则直接返回该函数。
 * 如果参数是字符串，则根据指定的字符串进行日期格式的映射，并返回一个根据指定格式格式化日期的函数。
 * 如果参数既不是函数也不是字符串，将抛出一个错误。
 *
 * @param {Function|string} formatter - 用于格式化的函数或字符串。
 * @returns {Function} 返回一个格式化函数，该函数接受一个包含日期信息的对象作为参数，并返回格式化后的字符串。
 * @throws {Error} 如果formatter既不是函数也不是字符串，则抛出错误。
 */
function _formateNormalize(formatter) {
  // 检查formatter是否为函数，如果是直接返回
  if (typeof formatter === "function") {
    return formatter;
  }
  // 如果formatter不是字符串，抛出错误
  if (typeof formatter !== "string") {
    throw new Error("formatter must be a function or string");
  }
  // 根据特定的字符串值映射到预定义的日期格式
  if (formatter === "date") {
    formatter = "yyyy-MM-dd";
  } else if (formatter === "datetime") {
    formatter = "yyyy-MM-dd HH:mm:ss";
  }
  // 返回一个根据预定义格式格式化日期的函数
  return (dateInfo) => {
    const { yyyy, MM, dd, HH, mm, ss } = dateInfo;
    // 使用正则替换将日期信息插入到格式字符串中
    return formatter
      .replace(/yyyy/g, yyyy)
      .replace(/MM/g, MM)
      .replace(/dd/g, dd)
      .replace(/HH/g, HH)
      .replace(/mm/g, mm)
      .replace(/ss/g, ss);
  };
}
/**
 * 格式化日期。
 * @param {Date} date - 需要格式化的日期对象。
 * @param {String} formatter - 日期格式化字符串，使用占位符来表示日期的各个部分，比如'yyyy-MM-dd'。
 * @param {Boolean} [isPad=false] - 是否在数字前补0，以便保证结果的位数。比如月份如果不是两位数，补0变为'01'到'12'。
 * @returns {String} 格式化后的日期字符串。
 */
function formate(date, formatter, isPad = false) {
  formatter = _formateNormalize(formatter); // 标准化格式化器

  // 根据isPad补0或不补0
  function _pad(value, length) {
    if (isPad) {
      return value.toString().padStart(length, "0");
    } else {
      return value.toString();
    }
  }

  // 提取日期中的各部分信息
  const dateInfo = {
    year: date.getFullYear(),
    month: date.getMonth() + 1,
    day: date.getDate(),
    hour: date.getHours(),
    minute: date.getMinutes(),
    second: date.getSeconds(),
    millisecond: date.getMilliseconds(),
  };

  // 对年月日时分秒进行补0操作，以满足格式化字符串的需求
  dateInfo.yyyy = _pad(dateInfo.year, 4);
  dateInfo.MM = _pad(dateInfo.month, 2);
  dateInfo.dd = _pad(dateInfo.day, 2);
  dateInfo.HH = _pad(dateInfo.hour, 2);
  dateInfo.mm = _pad(dateInfo.minute, 2);
  dateInfo.ss = _pad(dateInfo.second, 2);

  // 使用格式化器格式化日期信息，并返回结果
  return formatter(dateInfo);
}

formate(new Date(), "date");

formate(new Date(), "datetime");
formate(new Date(), "yyyy-MM-dd HH:mm:ss");
formate(new Date(), "date", true);
formate(new Date(), "datetime", true);
